package cn.wp.ewallet.wallet.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.wp.common.core.authentication.AuthenticationContext;
import cn.hutool.core.date.DateUtil;
import cn.wp.common.core.dto.WpResult;
import cn.wp.common.core.id.Id;
import cn.wp.common.core.model.WpPage;
import cn.wp.common.core.util.BeanUtil;
import cn.wp.ewallet.common.model.dto.MoneyTypeInfoDto;
import cn.wp.ewallet.common.service.MoneyTypeInfoService;
import cn.wp.ewallet.wallet.dao.*;
import cn.wp.ewallet.wallet.extension.constant.EwalletWalletConstant;
import cn.wp.ewallet.wallet.extension.util.EwalletWalletUtil;
import cn.wp.ewallet.wallet.model.dto.*;
import cn.wp.ewallet.wallet.model.dto.MentionMoneyInfoDto;
import cn.wp.ewallet.wallet.model.dto.MoneyChangeParamDto;
import cn.wp.ewallet.wallet.model.dto.UserWalletRelDto;
import cn.wp.ewallet.wallet.model.entity.*;
import cn.wp.ewallet.wallet.service.*;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.web3j.utils.Convert;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import static cn.wp.ewallet.wallet.extension.constant.EwalletWalletConstant.*;


/**
 * 提币申请单-业务逻辑层实现
 * Created by zhangyongli on 2020/01/09
 */
@Slf4j
@Service
public class MentionMoneyInfoServiceImpl implements MentionMoneyInfoService {

    //提币
    @Autowired
    private MentionMoneyInfoDao mentionMoneyInfoDao;

    //处理记录 dao
    @Autowired
    private ProcRecordInfoDao procRecordInfoDao;
    //申请单 dao
    @Autowired
    private ReqOrderInfoDao reqOrderInfoDao;

    @Autowired
    private AccountInfoDao accountInfoDao;

    @Autowired
    private AccountInfoService accountInfoService;

    @Autowired
    private MoneyTypeInfoService moneyTypeInfoService;

    @Autowired
    private UserWalletRelService userWalletRelService;

    @Autowired
    private AuthenticationContext authenticationContext;

    @Autowired
    private WebTransferService webTransferService;

    /**
     * 提币申请 （增加提币申请记录、提币记录）
     *
     * @param moneyChangeParamDto
     * @return
     */
    @Transactional
    @Override
    public WpResult applyMentionMoneyOrder(MoneyChangeParamDto moneyChangeParamDto) {

        //校验地址合法性
        if (!EwalletWalletUtil.isETHValidAddress(moneyChangeParamDto.getToWalletAddress())) {
            return new WpResult().failure(CHECK_ADDRESS_FAILED_CODE, "地址错误");
        }

        UserWalletRelDto userWalletRelDto = userWalletRelService.getUserWalletRel();
        //查询提币账户的信息
        AccountInfoDoo accountInfoDoo = accountInfoDao.selectAccountByWalletAddMoneyTypeId(userWalletRelDto.getWalletAddress(), moneyChangeParamDto.getMoneyTypeId(), ACCOUNT_TYPE_AVAILABLE);
        //账户可用金额
        BigDecimal startFromWallet = accountInfoDoo.getMoneyAmount();
        //提币金额
        BigDecimal money = EwalletWalletUtil.toLowerWei(moneyChangeParamDto.getMoney(), new BigDecimal(accountInfoDoo.getMoneyTypeUnit()));
        //手续费
        BigDecimal serviceCharge = EwalletWalletUtil.toLowerWei(moneyChangeParamDto.getServiceCharge(), new BigDecimal(accountInfoDoo.getMoneyTypeUnit()));
        //提币总计
        BigDecimal totalAmount = money.add(serviceCharge);
        //提币后的金额 = 账户可用金额 -（转账金额+手续费）
        BigDecimal afterFromWallet = startFromWallet.subtract(totalAmount);

        //提币金额不大大于可用资产
        if (-1 == afterFromWallet.compareTo(BigDecimal.valueOf(0))) {
            return new WpResult().failure(CHECK_AMOUNT_FAILED_CODE, "账户余额不足");
        } else {
            String reqOrderId = Id.gen();
            //新增申请单
            ReqOrderInfoDo reqOrderInfoDo = new ReqOrderInfoDo(reqOrderId, REQ_ORDER_TYPE_MENTIONMONEY, REQ_ORDER_STATUS_PENDING, new Date(),
                    userWalletRelDto.getWalletAddress(), accountInfoDoo.getAccountId(), moneyChangeParamDto.getRemark());
            reqOrderInfoDao.insertReqOrderInfo(reqOrderInfoDo);
            //新增提币申请单
            MentionMoneyInfoDo mentionMoneyInfoDo = new MentionMoneyInfoDo(reqOrderId, money, userWalletRelDto.getWalletAddress(), serviceCharge);
            mentionMoneyInfoDao.insertMentionMoneyInfo(mentionMoneyInfoDo);
            //保存资产变动信息
            accountInfoService.saveAccountChange(userWalletRelDto.getWalletAddress(), totalAmount, ACCOUNT_SUBTRACT, ACCOUNT_CHARGE_CASE_WITHFRAW_MONEY, moneyChangeParamDto.getMoneyTypeId(), ACCOUNT_TYPE_AVAILABLE);

            return new WpResult().success();
        }
    }

    /**
     * 提币申请单 处理逻辑
     *
     * @param mentionMoneyDto
     * @return
     */
    @Transactional
    @Override
    public synchronized WpResult handlerMentionMoneyOrder(MentionMoneyDto mentionMoneyDto) {

        //查询订单
        MentionMoneyInfoDoo mentionMoneyInfoDoo = mentionMoneyInfoDao.selectMentionMoneyReqOrderById(mentionMoneyDto.getReqOrderId());
        if (null == mentionMoneyInfoDoo || !EwalletWalletConstant.REQ_ORDER_STATUS_PENDING.equals(mentionMoneyInfoDoo.getOrderStatus())) {
            log.info("订单不存在，或者订单状态不是待处理，订单ID={}", mentionMoneyInfoDoo.getReqOrderId());
            return new WpResult().failure(1,"订单不存在或者订单状态不是待处理");
        }

        String userId = authenticationContext.currentUser().getId();

        //如果是提币失败，需要回退资金
        if (ACCOUNT_CHANGE_FAILED.equals(mentionMoneyDto.getOperResult())) {
            //回退提币数量，需要回退的资金=提币数量+手续费
            BigDecimal backAmount = mentionMoneyInfoDoo.getMentionAmount().add(mentionMoneyInfoDoo.getServiceCharge());
            accountInfoService.saveAccountChange(mentionMoneyInfoDoo.getWalletAddress(), backAmount, ACCOUNT_ADD, ACCOUNT_CHARGE_CASE_WITHFRAW_MONEY, mentionMoneyInfoDoo.getMoneyTypeId(), ACCOUNT_TYPE_AVAILABLE);
        }else {
            //发起区块链转账
            boolean transferResult = mentionMoneyByWeb(mentionMoneyDto.getReqOrderId());

            if (!transferResult) {
                return new WpResult().failure();
            }
        }

        //记录处理结果
        ProcRecordInfoDo procRecordInfoDo = new ProcRecordInfoDo(Id.gen(), mentionMoneyDto.getReqOrderId(), mentionMoneyDto.getOperResult(), userId, new Date(), mentionMoneyDto.getOperRemark());
        procRecordInfoDao.saveProcRecordInfo(procRecordInfoDo);

        //更新申请单状态
        ReqOrderInfoDo reqOrderInfoDo = new ReqOrderInfoDo();
        reqOrderInfoDo.setOrderStatus(REQ_ORDER_STATUS_FINISH);
        reqOrderInfoDo.setReqOrderId(mentionMoneyDto.getReqOrderId());
        reqOrderInfoDo.setRemark(mentionMoneyDto.getOperRemark());
        reqOrderInfoDao.updateReqOrderInfo(reqOrderInfoDo);
        return new WpResult().success();
    }

    /**
     * 中央账户向提币地址转账
     * @param reqOrderId
     * @return
     */
    private Boolean mentionMoneyByWeb(String reqOrderId) {
        //查询申请提币信息
        MentionMoneyInfoDoo mentionMoneyInfoDoo = mentionMoneyInfoDao.selectMentionMoneyReqOrderById(reqOrderId);
        if (null == mentionMoneyInfoDoo) {
            return false;
        }

        MoneyTypeInfoDto moneyTypeInfoDto = moneyTypeInfoService.getMoneyTypeInfoByMoneyTypeId(mentionMoneyInfoDoo.getMoneyTypeId());

        return webTransferService.webTransferFromCenter(mentionMoneyInfoDoo.getWalletAddress(), mentionMoneyInfoDoo.getMentionAmount().toBigInteger(), moneyTypeInfoDto.getContractAddress(), moneyTypeInfoDto.getMoneyTypeId(), reqOrderId,"提币");

    }

    @Override
    public MentionMoneyInfoDto getMentionMoneyReqOrderById(String orderId) {
        MentionMoneyInfoDoo mentionMoneyInfoDoo = mentionMoneyInfoDao.selectMentionMoneyReqOrderById(orderId);
        AccountInfoDoo accountInfoDoo=accountInfoDao.selectAccountInfoDooByAccountId(mentionMoneyInfoDoo.getAccountId());
        return BeanUtil.copyProperties(mentionMoneyInfoDoo, MentionMoneyInfoDto.class, new BeanUtil.Processor<MentionMoneyInfoDoo,MentionMoneyInfoDto>() {
            @Override
            public void convert(MentionMoneyInfoDoo source, MentionMoneyInfoDto target) {
                if (null != source.getMentionAmount()) {
                    target.setMentionAmount(EwalletWalletUtil.toUpperWei(source.getMentionAmount(), new BigDecimal(accountInfoDoo.getMoneyTypeUnit())));
                    target.setTotal(target.getMentionAmount());
                }else {
                    target.setTotal(BigDecimal.valueOf(0));
                }
                if (null != source.getServiceCharge()) {
                    target.setServiceCharge(EwalletWalletUtil.toUpperWei(source.getServiceCharge(), new BigDecimal(accountInfoDoo.getMoneyTypeUnit())));
                    target.setTotal(target.getTotal().add(target.getServiceCharge()));
                }
                //矿工费
                if (null != source.getTransferServiceCharge()) {
                    target.setTransferServiceCharge(Convert.fromWei(source.getTransferServiceCharge(), Convert.Unit.ETHER).toPlainString());
                }
                //申请时间
                target.setCreateDate(DateUtil.formatDateTime(source.getCreateDate()));
                //处理时间
                target.setOperTime(DateUtil.formatDateTime(source.getOperTime()));
                //转账时间
                target.setTransTime(DateUtil.formatDateTime(source.getTransTime()));
            }
        });
    }

    @Override
    public WpPage<MentionMoneyInfoDto> getMentionMoneyReqOrderByAddress(WpPage page) {
        UserWalletRelDto userWalletRelDto = userWalletRelService.getUserWalletRel();
        WpPage<MentionMoneyInfoDoo> wpPage = mentionMoneyInfoDao.selectMentionMoneyReqOrderByAddress(userWalletRelDto.getWalletAddress(), page);
        //查询SHQC数据精度
        MoneyTypeInfoDto shqc = moneyTypeInfoService.getMoneyTypeInfoByMoneyTypeId(MONEY_TYPE_SHQC);
        BigDecimal shqcUnit = BigDecimal.valueOf(Long.valueOf(shqc.getMoneyTypeUnit()));
        //查询USDT数据精度
        MoneyTypeInfoDto usdt = moneyTypeInfoService.getMoneyTypeInfoByMoneyTypeId(MONEY_TYPE_USDT);
        BigDecimal usdtUnit = BigDecimal.valueOf(Long.valueOf(usdt.getMoneyTypeUnit()));

        return wpPage.doConvertDto(MentionMoneyInfoDto.class, new BeanUtil.Processor<MentionMoneyInfoDoo,MentionMoneyInfoDto>() {
            @Override
            public void convert(MentionMoneyInfoDoo source, MentionMoneyInfoDto target) {
                if (MONEY_TYPE_USDT.equals(source.getMoneyTypeId())) {
                    if (null != source.getMentionAmount()) {
                        target.setMentionAmount(EwalletWalletUtil.toUpperWei(source.getMentionAmount(), usdtUnit));
                        target.setTotal(target.getMentionAmount());
                    }else {
                        target.setTotal(BigDecimal.valueOf(0));
                    }

                    if (null != source.getServiceCharge()) {
                        target.setServiceCharge(EwalletWalletUtil.toUpperWei(source.getServiceCharge(), usdtUnit));
                        target.setTotal(target.getTotal().add(target.getServiceCharge()));
                    }

                }else if (MONEY_TYPE_SHQC.equals(source.getMoneyTypeId())) {
                    if (null != source.getMentionAmount()) {
                        target.setMentionAmount(EwalletWalletUtil.toUpperWei(source.getMentionAmount(), shqcUnit));
                        target.setTotal(target.getMentionAmount());
                    }else {
                        target.setTotal(BigDecimal.valueOf(0));
                    }

                    if (null != source.getServiceCharge()) {
                        target.setServiceCharge(EwalletWalletUtil.toUpperWei(source.getServiceCharge(), shqcUnit));
                        target.setTotal(target.getTotal().add(target.getServiceCharge()));
                    }
                }

                if (null != source.getTransferServiceCharge()) {
                    target.setTransferServiceCharge(Convert.fromWei(source.getTransferServiceCharge(), Convert.Unit.ETHER).toPlainString());
                }

                //申请时间
                target.setCreateDate(DateUtil.formatDateTime(source.getCreateDate()));
                //处理时间
                target.setOperTime(DateUtil.formatDateTime(source.getOperTime()));
                //转账时间
                target.setTransTime(DateUtil.formatDateTime(source.getTransTime()));
            }
        });
    }

    @Override
    public WpPage<MentionMoneyInfoDto> getMentionMoneyReqOrderByMoneyType(String startTime, String endTime, String moneyType, WpPage page) {
        WpPage<MentionMoneyInfoDoo> wpPage = mentionMoneyInfoDao.getMentionMoneyReqOrderByMoneyType(startTime, endTime, moneyType,  page);
        //查询SHQC数据精度
        MoneyTypeInfoDto money = moneyTypeInfoService.getMoneyTypeInfoByMoneyTypeId(moneyType);
        BigDecimal unit = BigDecimal.valueOf(Long.valueOf(money.getMoneyTypeUnit()));

        return wpPage.doConvertDto(MentionMoneyInfoDto.class, new BeanUtil.Processor<MentionMoneyInfoDoo,MentionMoneyInfoDto>() {
            @Override
            public void convert(MentionMoneyInfoDoo source, MentionMoneyInfoDto target) {
                if (null != source.getMentionAmount()) {
                    target.setMentionAmount(EwalletWalletUtil.toUpperWei(source.getMentionAmount(), unit));
                    target.setTotal(target.getMentionAmount());
                }else {
                    target.setTotal(BigDecimal.valueOf(0));
                }

                if (null != source.getServiceCharge()) {
                    target.setServiceCharge(EwalletWalletUtil.toUpperWei(source.getServiceCharge(), unit));
                    target.setTotal(target.getTotal().add(target.getServiceCharge()));
                }

                if (null != source.getTransferServiceCharge()) {
                    target.setTransferServiceCharge(Convert.fromWei(source.getTransferServiceCharge(), Convert.Unit.ETHER).toPlainString());
                }

                //申请时间
                target.setCreateDate(DateUtil.formatDateTime(source.getCreateDate()));
                //处理时间
                target.setOperTime(DateUtil.formatDateTime(source.getOperTime()));
                //转账时间
                target.setTransTime(DateUtil.formatDateTime(source.getTransTime()));
            }
        });
    }


}